/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package view;

import java.util.Scanner;
import model.Const;

/**
 * Display class to provide command line user interface that displays menu and the simulation result.
 * @author Chiaki Sato
 * @since J2SDK 1.6
 * @version 1.0
 */
public class DisplayCmd {

    // Const
    private final int MENU_MAIN_HORIZ_LANES_NUM = 1;
    private final int MENU_MAIN_VERT_LANES_NUM = 2;
    private final int MENU_MAIN_PROBABILITY_ENTER_HORIZ = 3;
    private final int MENU_MAIN_PROBABILITY_ENTER_VERT = 4;
    private final int MENU_MAIN_RUN_ONE_SIMULATION = 5;
    private final int MENU_MAIN_RUN_CYCLES_SIMULATION = 6;
    private final int MENU_MAIN_QUIT_PROGRAM = 7;
    private final int MENU_SUB_RUN_ONE_SIMULATION = 5;
    private final int MENU_SUB_RUN_CYCLES_SIMULATION = 6;
    private final int MENU_SUB_RETURN_MAIN_MENU = 7;

    // private instance valiables
    private int nLanesHoriz;
    private int nLanesVert;
    private float fProbabilityHoriz;
    private float fProbabilitynVert;
    private int nSimulationCyleCount;
    private boolean isRetry;

    /**
     * Default constructor of display class, initialize instance valuables.
     */
    public DisplayCmd(){

        // init instance valiables
        nLanesHoriz = 1;
        nLanesVert = 1;
        fProbabilityHoriz = 0.5f;
        fProbabilitynVert = 0.5f;
        nSimulationCyleCount = 1;
        isRetry = true;
    }

    /**
     * Public function to show the main menu.
     * @return Execute command that was chosen by user
     */
    public int showMainMenu(){

        int nExecCommand = Const.COMMAND_EXIT_PROGRAM;
        isRetry = true;

        while (isRetry){

            // === MENU ===
            // 1. Set Number of horizontal lanes (H-street) [min 1, max 3]
            // 2. Set Number of vertical lanes (V-street) [min 1, max 4]
            // 3. Set Probability of a car entering H-street [min 0, max 1]
            // 4. Set Probability of a car entering V-street [min 0, max 1]
            // 5. Run one simulation cycle
            // 6. Set and run number of simulation cycles [min 1, max 10]
            // Enter your choice>" ;
            System.out.println("");
            System.out.println(Const.MENU_MAIN_TITLE);
            System.out.println(Const.MENU_MAIN_HORIZ_LANES_NUM);
            System.out.println(Const.MENU_MAIN_VERT_LANES_NUM);
            System.out.println(Const.MENU_MAIN_PROBABILITY_ENTER_HORIZ);
            System.out.println(Const.MENU_MAIN_PROBABILITY_ENTER_VERT);
            System.out.println(Const.MENU_MAIN_RUN_ONE_SIMULATION);
            System.out.println(Const.MENU_MAIN_RUN_CYCLES_SIMULATION);
            System.out.println(Const.MENU_MAIN_QUIT_PROGRAM);
            System.out.print(Const.MENU_ENTER_YOUR_CHOICE);

            nExecCommand = doMainMenuInput();
        }
        return nExecCommand;
    }

    /**
     * Public function to show the sub menu.
     * @return Execute command that was chosen by user
     */
    public int showSubMenu(){

        int nExecCommand = Const.COMMAND_EXIT_PROGRAM;
        isRetry = true;

        while (isRetry){

            // === SUB MENU ===
            // 1. Run one simulation cycle
            // 2. Set and run number of simulation cycles [min 1, max 10]
            // 3. Return to main memu
            // Enter your choice>" ;
            System.out.println("");
            System.out.println(Const.MENU_SUB_TITLE);
            System.out.println(Const.MENU_SUB_RUN_ONE_SIMULATION);
            System.out.println(Const.MENU_SUB_RUN_CYCLES_SIMULATION);
            System.out.println(Const.MENU_SUB_RETURN_MAIN_MENU);
            System.out.print(Const.MENU_ENTER_YOUR_CHOICE);

            nExecCommand = doSubMenuInput();
        }
        return nExecCommand;
    }

    /**
     * Private function to process user input from main menu.
     * @return Execute command that was chosen by user
     */
    private int doMainMenuInput(){

        int nMenuInput;
        int nExecCommand = Const.COMMAND_EXIT_PROGRAM;

        nMenuInput = getInputRangeValue(MENU_MAIN_HORIZ_LANES_NUM, MENU_MAIN_QUIT_PROGRAM);
        isRetry = true;

        switch (nMenuInput) {

            case MENU_MAIN_HORIZ_LANES_NUM:
                while (isRetry) {
                    System.out.println("");
                    System.out.println(Const.MENU_MAIN_HORIZ_LANES_NUM);
                    System.out.print(Const.MENU_ENTER_A_NUMBER);
                    nLanesHoriz = getInputRangeValue(Const.MIN_HORIZ_LANES, Const.MAX_HORIZ_LANES);
                }
                isRetry = true;
                break;

            case MENU_MAIN_VERT_LANES_NUM:
                while (isRetry) {
                    System.out.println("");
                    System.out.println(Const.MENU_MAIN_VERT_LANES_NUM);
                    System.out.print(Const.MENU_ENTER_A_NUMBER);
                    nLanesVert = getInputRangeValue(Const.MIN_VERT_LANES, Const.MAX_VERT_LANES);
                }
                isRetry = true;
                break;

            case MENU_MAIN_PROBABILITY_ENTER_HORIZ:
                while (isRetry) {
                    System.out.println("");
                    System.out.println(Const.MENU_MAIN_PROBABILITY_ENTER_HORIZ);
                    System.out.print(Const.MENU_ENTER_A_NUMBER);
                    fProbabilityHoriz = getInputRangeValueFloat(Const.MIN_PROBABILITY_HORIZ, Const.MAX_PROBABILITY_HORIZ);
                }
                isRetry = true;
                break;

            case MENU_MAIN_PROBABILITY_ENTER_VERT:
                while (isRetry) {
                    System.out.println("");
                    System.out.println(Const.MENU_MAIN_PROBABILITY_ENTER_VERT);
                    System.out.print(Const.MENU_ENTER_A_NUMBER);
                    fProbabilitynVert = getInputRangeValueFloat(Const.MIN_PROBABILITY_VERT, Const.MAX_PROBABILITY_VERT);
                }
                isRetry = true;
                break;

            case MENU_MAIN_RUN_ONE_SIMULATION:
                System.out.println("");
                nExecCommand = Const.COMMAND_RUN_ONE_SIMULATION;
                isRetry = false;
                break;

            case MENU_MAIN_RUN_CYCLES_SIMULATION:
                while (isRetry) {
                    System.out.println("");
                    System.out.println(Const.MENU_MAIN_RUN_CYCLES_SIMULATION);
                    System.out.print(Const.MENU_ENTER_A_NUMBER);
                    nSimulationCyleCount = getInputRangeValue(Const.MIN_RUN_CYCLES, Const.MAX_RUN_CYCLES);
                }
                nExecCommand = Const.COMMAND_RUN_CYCLES_SIMULATION;
                isRetry = false;
                break;

            case MENU_MAIN_QUIT_PROGRAM:
                System.out.print(Const.MENU_MAIN_MSG_GOODBYE);
                System.out.println("");
                nExecCommand = Const.COMMAND_EXIT_PROGRAM;
                isRetry = false;
                break;

        }

        return nExecCommand;
    }


    /**
     * Private function to process user input from sub menu.
     * @return Execute command that was chosen by user
     */
    private int doSubMenuInput(){

        int nMenuInput;
        int nExecCommand = Const.COMMAND_EXIT_PROGRAM;

        nMenuInput = getInputRangeValue(MENU_SUB_RUN_ONE_SIMULATION, MENU_SUB_RETURN_MAIN_MENU);
        isRetry = true;

        switch (nMenuInput) {

            case MENU_SUB_RUN_ONE_SIMULATION:
                System.out.println("");
                nExecCommand = Const.COMMAND_RUN_ONE_SIMULATION;
                isRetry = false;
                break;

            case MENU_SUB_RUN_CYCLES_SIMULATION:
                while (isRetry) {
                    System.out.println("");
                    System.out.println(Const.MENU_MAIN_RUN_CYCLES_SIMULATION);
                    System.out.print(Const.MENU_ENTER_A_NUMBER);
                    nSimulationCyleCount = getInputRangeValue(1, 10);
                }
                System.out.println("");
                nExecCommand = Const.COMMAND_RUN_CYCLES_SIMULATION;
                isRetry = false;
                break;
            case MENU_SUB_RETURN_MAIN_MENU:
                nExecCommand = Const.COMMAND_RETURN_MAIN_MENU;
                isRetry = false;
                break;
        }

        return nExecCommand;
    }

    /**
     * Private function to process user input from sub menu.
     * @param nMin minimum input value to be validated
     * @param nMax maximum input value to be validated
     * @return Validated user input value
     */
    private int getInputRangeValue(int nMin, int nMax) {

        Scanner oScan;
        int nInputValue = -1;

        try {

            while (isRetry){
                oScan = new Scanner(System.in);
                if (oScan.hasNextInt() == false){

                    System.out.println("");
                    System.out.println(Const.ERR_MSG_INPUT_NOT_NUMBER);
                    System.out.print(Const.MENU_ENTER_A_NUMBER);
                }
                else{

                    nInputValue = oScan.nextInt();
                    if(nInputValue < nMin || nMax < nInputValue) {

                        System.out.println("");
                        System.out.println(String.format(Const.ERR_MSG_INPUT_RANGE, nMin, nMax));
                        System.out.print(Const.MENU_ENTER_A_NUMBER);
                    }
                    else {
                        isRetry = false;
                    }
                }
            }
            return nInputValue;
        }
        catch (Exception e) {

            System.out.println("");
            System.out.println(Const.ERR_MSG_INPUT_NOT_NUMBER);
            isRetry = false;
        }
        finally{

            return nInputValue;
        }
    }

    /**
     * Private function to process user input from sub menu.
     * @param nMin minimum input value to be validated
     * @param nMax maximum input value to be validated
     * @return Validated user input value
     */
    private float getInputRangeValueFloat(int nMin, int nMax) {

        Scanner oScan;
        float fInputValue = -1;

        try {

            while (isRetry){
                oScan = new Scanner(System.in);
                if (oScan.hasNextFloat() == false){

                    System.out.println("");
                    System.out.println(Const.ERR_MSG_INPUT_NOT_NUMBER);
                    System.out.print(Const.MENU_ENTER_A_NUMBER);
                }
                else{

                    fInputValue = oScan.nextFloat();
                    if(fInputValue < nMin || nMax < fInputValue) {

                        System.out.println("");
                        System.out.println(String.format(Const.ERR_MSG_INPUT_RANGE, nMin, nMax));
                        System.out.print(Const.MENU_ENTER_A_NUMBER);
                    }
                    else {
                        isRetry = false;
                    }
                }
            }
            return fInputValue;
        }
        catch (Exception e) {

            System.out.println("");
            System.out.println(Const.ERR_MSG_INPUT_NOT_NUMBER);
            isRetry = false;
        }
        finally{

            return fInputValue;
        }
    }

    public void drawIntersectionTraffic(String stResultDrawString){

        System.out.println(stResultDrawString);
    }

    /**
     * Public function to get a value of instance variable.
     * @return Number of horizontal lanes
     */
    public int getLanesHoriz() {
        return nLanesHoriz;
    }

    /**
     * Public function to get a value of instance variable.
     * @return Number of vertical lanes
     */
    public int getLanesVert() {
        return nLanesVert;
    }

    /**
     * Public function to get a value of instance variable.
     * @return Probability of a car entering the horizontal street
     */
    public float getProbabilityHoriz() {
        return fProbabilityHoriz;
    }

    /**
     * Public function to get a value of instance variable.
     * @return Probability of cars entering the vertical street
     */
    public float getProbabilityVert() {
        return fProbabilitynVert;
    }

    /**
     * Public function to get a value of instance variable.
     * @return Number of simulation cycles
     */
    public int getSimulationCycleCount() {
        return nSimulationCyleCount;
    }
}
